Skip to content

Load local plugins and add isComponentInvocation - #64

Merged
johanrd merged 5 commits into
mainfrom
local-plugins
Oct 21, 2026
Merged

johanrd merged 5 commits into
mainfrom
local-plugins

Conversation

@johanrd

@johanrd johanrd commented Sep 30, 2026

Copy link
Copy Markdown
Owner

Makes project-specific rules possible with validate-gts.

  • validate-gts loads plugins from .htmlvalidate.json, e.g. "<rootDir>/lint/html-rules.mjs". Before, it failed with "Failed to load plugin".
  • Local plugin files are part of the report cache key, so editing a rule re-lints.
  • New export isComponentInvocation(element): true when an element stands in for a component call (<Modal> validated as <dialog>). A rule can use it to report in the component's own template only.

The README has a "Custom rules" section.

Cowritten by Claude

validate-gts now resolves plugins in .htmlvalidate.json through
html-validate's ESM resolver, with <rootDir> as the config's folder.
Local plugin files are part of the report cache key.

isComponentInvocation(element) tells a custom rule that an element
stands in for a component call, so it can skip call sites.
@johanrd johanrd added the enhancement New feature or request label Sep 30, 2026
isComponentInvocation re-read the file from disk and compared the written
tag with the parsed one, case-insensitively. That got <Button>, <Input>
and <Textarea> wrong, called svg <title>/<desc> invocations, shifted after
an emoji or BOM, and read stale text when the editor buffer differed from
disk.

The blanker now records the element start of every substitution
(componentInvocationOffsets), the transform's processElement hook marks
those elements, and isComponentInvocation reads the mark. Roots a
component writes with (element this.tag) are not marked.
validate-gts parsed .htmlvalidate.json itself, so './' paths resolved
against the working directory, and only `<rootDir>` plugin entry files
were in the report-cache key.

It now loads the config through esmResolver().resolveConfig(), which
expands './' against the config's folder, and substitutes `<rootDir>`
with a function replacer: html-validate reads a native path as a URL
unless it exists as written, so '#', '?', '%' and '$' broke loading.

The config resolves once before validating. A resolver wrapper records
every local file it loads (plugins, extends, elements, and local
transformers from the config), and the cache key holds the content of
those files and everything they import. A plugin that cannot load now
fails once with one message, and a non-object config falls back to the
defaults instead of crashing.
A rule that threw was caught per file and printed as "fatal", but the
file was not counted, so validate-gts exited 0 with "total errors: 0"
and dropped that file's results. The summary now reports those files
and the run exits 1.
@johanrd
johanrd merged commit e622572 into main Oct 21, 2026
4 checks passed
@github-actions github-actions Bot mentioned this pull request Oct 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant